#include "SWFStructs.h"
#include "SWFOutputStream.h"
#include "SWFEncoder.h"
#include "ByteArray.h"
#include <assert.h>

extern int minBits(int number, int bits);

void EndShapeRecord::encode( SWFOutputStream* stream, SWFEncodeContext* cxt, ShapeWithStyle* style ) const 
{
	stream->writeUB(TypeFlag, 1);
	stream->writeUB(0, 5);
}

void StyleChangeRecord::encode( SWFOutputStream* stream, SWFEncodeContext* cxt, ShapeWithStyle* style ) const 
{
	stream->writeUB(TypeFlag, 1);
	stream->writeUB(StateNewStyles, 1);
	stream->writeUB(StateLineStyle, 1);
	stream->writeUB(StateFillStyle1, 1);
	stream->writeUB(StateFillStyle0, 1);
	stream->writeUB(StateMoveTo, 1);

	if (StateMoveTo)
	{
		stream->writeUB(MoveBits, 5);
		stream->writeSB(MoveDeltaX, MoveBits);
		stream->writeSB(MoveDeltaY, MoveBits);
	}

	if (StateFillStyle0)
	{
		stream->writeUB(FillStyle0, style->NumFillBits);
	}

	if (StateFillStyle1)
	{
		stream->writeUB(FillStyle1, style->NumFillBits);
	}

	if (StateLineStyle)
	{
		stream->writeUB(LineStyle, style->NumLineBits);
	}

	if (StateNewStyles)
	{
		stream->alignBits();
		FillStyles->encode(stream, cxt);
		LineStyles->encode(stream, cxt);

		UB NumFillBits = minBits(FillStyles->FillStyles.size(), 0);
		UB NumLineBits = minBits(LineStyles->LineStyles.size(), 0);

		stream->writeUB(NumFillBits, 4);
		stream->writeUB(NumLineBits, 4);
		style->NumFillBits = NumFillBits;
		style->NumLineBits = NumLineBits;
	}
}

void StraightEdgeRecord::encode( SWFOutputStream* stream, SWFEncodeContext* cxt, ShapeWithStyle* style ) const 
{
	stream->writeUB(TypeFlag, 1);
	stream->writeUB(StraightFlag, 1);
	stream->writeUB(NumBits - 2, 4);
	stream->writeUB(GeneralLineFlag, 1);
	if (GeneralLineFlag == 0)
		stream->writeUB(VertLineFlag, 1);
	if (GeneralLineFlag == 1 || VertLineFlag == 0)
		stream->writeSB(DeltaX, NumBits);
	if (GeneralLineFlag == 1 || VertLineFlag == 1)
		stream->writeSB(DeltaY, NumBits);
}

void CurvedEdgeRecord::encode( SWFOutputStream* stream, SWFEncodeContext* cxt, ShapeWithStyle* style ) const 
{
	stream->writeUB(TypeFlag, 1);
	stream->writeUB(StraightFlag, 1);
	stream->writeUB(NumBits - 2, 4);
	stream->writeSB(ControlDeltaX, NumBits);
	stream->writeSB(ControlDeltaY, NumBits);
	stream->writeSB(AnchorDeltaX, NumBits);
	stream->writeSB(AnchorDeltaY, NumBits);
}

void FillStyle::encode( SWFOutputStream* stream, SWFEncodeContext* cxt ) const
{
	stream->writeUI8(FillStyleType);
	//Color
	if (this->FillStyleType == 0x00)
	{
		switch (cxt->defineShapeCode)
		{
		case DefineShape:
		case DefineShape2:
			stream->writeColorRGB(this->Color);
			break;
		case DefineShape3:
		case DefineShape4:
			stream->writeColorRGBA(this->Color);
			break;
		}
	}

	//GradientMatrix
	if (this->FillStyleType == 0x10 ||
		this->FillStyleType == 0x12 ||
		this->FillStyleType == 0x13)
	{
		stream->writeMatrix(this->GradientMatrix);
	}

	//Gradient
	if (this->FillStyleType == 0x10 ||
		this->FillStyleType == 0x12)
	{
		assert(this->Gradient != nullptr);
		stream->writeUB(Gradient->SpreadMode, 2);
		stream->writeUB(Gradient->InterpolationMode, 2);
		stream->writeUB(Gradient->NumGradients, 4);

		for (UB i = 0; i < Gradient->NumGradients; ++i)
		{
			GradRecord* gr = Gradient->GradientRecords[i];
			stream->writeUI8(gr->Ratio);
			if (cxt->defineShapeCode == DefineShape3 || cxt->defineShapeCode == DefineShape4)
			{
				stream->writeColorRGBA(gr->Color);
			}
			else
			{
				stream->writeColorRGB(gr->Color);
			}
		}
	}
	else if (this->FillStyleType == 0x13)
	{
		//FocalGradient;
		assert(this->Gradient != nullptr);
		stream->writeUB(Gradient->SpreadMode, 2);
		stream->writeUB(Gradient->InterpolationMode, 2);
		stream->writeUB(Gradient->NumGradients, 4);

		for (UB i = 0; i < Gradient->NumGradients; ++i)
		{
			GradRecord* gr = Gradient->GradientRecords[i];
			stream->writeUI8(gr->Ratio);
			if (cxt->defineShapeCode == DefineShape3 || cxt->defineShapeCode == DefineShape4)
			{
				stream->writeColorRGBA(gr->Color);
			}
			else
			{
				stream->writeColorRGB(gr->Color);
			}
		}
		FocalGradient* fGradient = dynamic_cast<FocalGradient*>(Gradient);
		stream->writeFixed8(fGradient->FocalPoint);
	}

	//Bitmap id
	if (this->FillStyleType == 0x40 ||
		this->FillStyleType == 0x41 ||
		this->FillStyleType == 0x42 ||
		this->FillStyleType == 0x43)
	{
		stream->writeUI16(cxt->getCharacterId(this->Bitmap));
		//stream->writeUI16(this->BitmapId);
		stream->writeMatrix(this->BitmapMatrix);
	}
}

void FillStyleArray::encode( SWFOutputStream* stream, SWFEncodeContext* cxt ) const
{
	UI8 count = FillStyles.size();
	if (count >= 0xff)
	{
		stream->writeUI8(0xff);
		stream->writeUI16(count);
	}
	else
	{
		stream->writeUI8(count);
	}
	for (auto& style : FillStyles)
	{
		style->encode(stream, cxt);
	}
}

void LineStyle::encode( SWFOutputStream* stream, SWFEncodeContext* cxt ) const
{
	if (cxt->defineShapeCode == DefineShape4)
	{
		assert(false);
	}
	else
	{
		stream->writeUI16(Width);
		if (cxt->defineShapeCode == DefineShape3)
			stream->writeColorRGBA(Color);
		else
			stream->writeColorRGB(Color);
	}
}

void LineStyle2::encode( SWFOutputStream* stream, SWFEncodeContext* cxt ) const 
{
	stream->writeUI16(this->Width);
	stream->writeUB(this->StartCapStyle, 2);
	stream->writeUB(this->JoinStyle, 2);
	stream->writeUB(this->HasFillFlag, 1);
	stream->writeUB(this->NoHScaleFlag, 1);
	stream->writeUB(this->NoVScaleFlag, 1);
	stream->writeUB(this->PixelHintingFlag, 1);
	//Reserved
	stream->writeUB(0, 5);
	stream->writeUB(this->NoClose, 1);
	stream->writeUB(this->EndCapStyle, 2);
	if (this->JoinStyle == 2)
		stream->writeUI16(this->MiterLimitFactor);
	if (this->HasFillFlag == 0)
		stream->writeColorRGBA(Color);
	else
		FillType->encode(stream, cxt);
}

void LineStyleArray::encode( SWFOutputStream* stream, SWFEncodeContext* cxt ) const
{
	UI8 count = LineStyles.size();
	if (count >= 0xff)
	{
		stream->writeUI8(0xff);
		stream->writeUI16(count);
	}
	else
	{
		stream->writeUI8(count);
	}
	for (auto& style : LineStyles)
	{
		style->encode(stream, cxt);
	}
}

void Gradient::encode( SWFOutputStream* stream, SWFEncodeContext* cxt ) const
{
	assert(false);
}

void FocalGradient::encode( SWFOutputStream* stream, SWFEncodeContext* cxt ) const 
{
	assert(false);
}

void FilterList::decode(SWFInputStream* stream)
{
	this->NumberOfFilters = stream->readUI8();
	for (UI8 i = 0; i < NumberOfFilters; ++i)
	{
		UI8 filerId = stream->readUI8();
		FilterType type = (FilterType)filerId;
		Filter* filter = nullptr;
		switch (type)
		{
		case FilterType::DROPSHADOWFILTER:
			break;
		case FilterType::BLURFILTER:
			filter = new BlurFilter();
			break;
		case FilterType::GLOWFILTER:
			filter = new GlowFilter();
			break;
		case FilterType::BEVELFILTER:
			break;
		case FilterType::GRADIENTGLOWFILTER:
			break;
		case FilterType::CONVOLUTIONFILTER:
			break;
		case FilterType::COLORMATRIXFILTER:
			filter = new ColorMatrixFilter();
			break;
		case FilterType::GRADIENTBEVELFILTER:
			break;
		}
		
		filter->FilterID = filerId;
		filter->decode(stream);
		this->list.push_back(filter);
	}
}

void FilterList::encode( SWFOutputStream* stream )
{
	stream->writeUI8(this->NumberOfFilters);
	for (Filter* f : list)
	{
		stream->writeUI8(f->FilterID);
		f->encdoe(stream);
	}
}

void ColorMatrixFilter::decode(SWFInputStream* stream)
{
	stream->alignBits();
	stream->readByteArray(&matrixData, 80);
	/*
	for (int i = 0; i < 20; ++i)
	{
		this->matrix[i] = stream->readFloat();
	}
	*/
}

void ColorMatrixFilter::encdoe( SWFOutputStream* stream )
{
	stream->writeByteArray(&matrixData);
	/*
	for (int i = 0; i < 20; ++i)
	{
		stream->writeFloat(matrix[i]);
	}
	*/
}

void ConvolutionFilter::decode(SWFInputStream* stream)
{
	this->matrixX = stream->readUI8();
	this->matrixY = stream->readUI8();
	this->divior = stream->readFloat();
	this->bias = stream->readFloat();
	UI32 matrixSize = matrixX * matrixY;
	for (UI32 i = 0; i < matrixSize; ++i)
	{
		this->matrix.push_back(stream->readFloat());
	}
	this->defaultColor = stream->readColorRGBA();
	this->reserved = stream->readUB(6);
	this->clamp = stream->readUB(1);
	this->preserveAlpha = stream->readUB(1);
}

void ConvolutionFilter::encdoe( SWFOutputStream* stream )
{
	assert(false);
}

void BlurFilter::decode(SWFInputStream* stream)
{
	assert(false);
}

void BlurFilter::encdoe( SWFOutputStream* stream )
{
	assert(false);
}

void GlowFilter::decode(SWFInputStream* stream)
{
	stream->alignBits();
	this->GolwColor = stream->readColorRGBA();
	this->BlurX = stream->readFixed();
	this->BlurY = stream->readFixed();
	this->Strength = stream->readFixed8();
	this->InnerGlow = stream->readUB(1);
	this->Knockout = stream->readUB(1);
	this->CompositeSource = stream->readUB(1);
	this->Passes = stream->readUB(5);
}

void GlowFilter::encdoe( SWFOutputStream* stream )
{
	stream->writeColorRGBA(this->GolwColor);
	stream->writeFixed(BlurX);
	stream->writeFixed(BlurY);
	stream->writeFixed8(Strength);
	stream->writeUB(InnerGlow, 1);
	stream->writeUB(Knockout, 1);
	stream->writeUB(CompositeSource, 1);
	stream->writeUB(Passes, 5);
}
